#ifndef LOAD_AND_SHOW_SCREEN_LOD_H
#define LOAD_AND_SHOW_SCREEN_LOD_H

#include <iostream>
#include <vector>
#include <utility>
#include <GL/gl.h>
#include "object3d.h"
#include "vertex.h"
#include "lighting.h"
#include "vect.h"

class load_and_show_screen
{
   public:
   int keyboard_func(unsigned char key, int x, int y);
   void display_func(int width, int height);
   void mouse_func(int button, int state, int x, int y);
   void mouse_motion_func(int x, int y);

   load_and_show_screen(lighting& l, int& polygonMode) : 
         userEnteringValue(false), light(l),
         scenezoom(1.0), scenerot(0.0,0.0,0.0), scenetransl(0.0,0.0,0.0),
         dispo(MAX_DISPLAYED_LODS), fit(MAX_DISPLAYED_LODS),
         mouseMode(mouse_mode::rotate), mouseOn(false),
         polygon_mode(polygonMode)
         
   {
      dispo.activate_whole_scene();
      polygon_mode=GL_LINE;//wired
   }

   private:
   void draw3d(int width, int height);
   void find_ranges();
   void reset_view();
   void find_transl_to_origin();
   void change_polygon_mode();
   void setMenuColor()
   {
       glColor3f(menuPosColorR, menuPosColorG, menuPosColorB);
   }

   void setActMenuColor()
   {
       glColor3f(actMenuPosColorR, actMenuPosColorG, actMenuPosColorB);
   }
   
   void setObjColor()
   {
      glColor3f(objColorR, objColorG, objColorB);
   }

   void setActiveObjColor()
   {
      glColor3f(activeObjColorR, activeObjColorG, activeObjColorB);
   }

   struct mouse_mode
   {
      enum {
         rotate,
         move,
         zoom
      };
   };

   class display_observer
   {
      public:
      void put_on_display(unsigned position, unsigned object)
      {
         assert(position < displayed.size());
         displayed[position] = object;
         displodscount++;
         //active[position] = false;
         activate_whole_scene();
      }
      void take_from_display(unsigned position)
      {
         assert(position < displayed.size());
         unsigned end = displayed.size();
         unsigned i=position+1;
         for(; i<end && i<displodscount; i++)
            displayed[i]=displayed[i-1];

         displodscount--;
         activate_whole_scene();
      }
      unsigned displayed_count()
      {
         return displodscount;
      }
      std::vector<unsigned> displayed_objects()
      {
         std::vector<unsigned> ret(displodscount);
         for(unsigned i=0; i<displodscount; i++)
            ret[i] = displayed[i];
         return ret;
      }

      bool active_object(unsigned position)
      {
         assert(position < active.size());
         assert(position < displodscount);
         return active[position];
      }
      void activate_whole_scene()
      {
         unsigned end = active.size()-1;
         for(unsigned i=0; i<end; i++)
            active[i]=false;
         active[end]=true;
      }
      bool whole_scene_active()
      {
         return active.back();
      }
      void reset()
      {
         displodscount=0;
         activate_whole_scene();
      }

      display_observer(unsigned screen_capacity) : displayed(screen_capacity),
         active(screen_capacity+1) // the scene itself is also treaded as an object (+1)
      {
         reset();
      }
      
      private:
      std::vector<unsigned> displayed;
      std::vector<bool> active;
      unsigned displodscount;
   };

   class fitter
   {
      public:
      void put(unsigned position, unsigned object, const vect& gmin, const vect& gmax,
               const vect& omin, const vect& omax)
      {
         assert(position<transl.size() && position<scale.size());
         assert(gmax.x!=gmin.x && gmax.y!=gmin.y && gmax.z!=gmin.z);//possible division by 0
                                                               // below
         transl[position].x = gmin.x+(gmax.x-gmin.x)/2;
         transl[position].y = gmin.y+(gmax.y-gmin.y)/2;
         transl[position].z = gmin.z+(gmax.z-gmin.z)/2;

         //we're bounding with cubes, so boundxrange==boundyrange=boundzrange
         float boundxrange = gmax.x-gmin.x;

         float xrange = (omax.x-omin.x);
         float yrange = (omax.y-omin.y);
         float zrange = (omax.z-omin.z);
         float maxrange = xrange>yrange ? xrange : yrange;
         maxrange=maxrange>zrange ? maxrange : zrange;
         assert(maxrange!=0.0);

         //rescale*maxrange=boundxrange
         scale[position] = boundxrange/maxrange;
      }

      vect get_transl(unsigned position)
      {
         assert(position < transl.size());
         return transl[position];
      }

      float get_scale(unsigned position)
      {
         assert(position < scale.size());
         return scale[position];
      }

      fitter(unsigned displayed_count) : transl(displayed_count),
                                         scale(displayed_count)
      {
      }
      private:
      // tells how to translate displayed objects
      // from origin to the centers of grids
      std::vector<vect> transl;
      // stores factors to scale objects to fit grids
      std::vector<float> scale;
   };



   std::vector< object3dv<vertex> > lods;
   std::vector< std::pair<vect,vect> > ranges;
   std::string filename;
   std::string loaded_filename;
   bool userEnteringValue;
   lighting& light;

   float scenezoom;
   vect scenerot;
   vect scenetransl;

   display_observer dispo;
   fitter fit;
   std::vector<vect> transl_to_origin;

   int mouseMode;
   bool mouseOn;
   int prev_mouse_x;
   int prev_mouse_y;

   int& polygon_mode;


   static const int MAX_FILENAME_LENGTH = 64;
   static const int MAX_DISPLAYED_LODS = 4;
   static const float INFINITY_COORDINATE = 1e8;

   static const float menuPosColorR = 1.0f;
   static const float menuPosColorG = 1.0f;
   static const float menuPosColorB = 1.0f;

   static const float actMenuPosColorR = 0.1f;
   static const float actMenuPosColorG = 1.0f;
   static const float actMenuPosColorB = 0.4f;

   static const float objColorR = 0.4f;
   static const float objColorG = 0.81f;
   static const float objColorB = 0.61f;

   static const float activeObjColorR = 0.8f;
   static const float activeObjColorG = 0.5f;
   static const float activeObjColorB = 0.9f;

   static const std::pair<vect,vect> obj_pos[];
   static const int obj_pos_idx[];
   
   static const std::pair<float,float> label_pos[];

   static const float pixelChangeRot = 3;
   static const float pixelChangeTransl = 0.02f;
   static const float pixelChangeZoom = 0.005f;
   
   
};

#endif
